package org.apache.james.hibernate;

import java.io.IOException;
import java.util.Date;
import java.util.Enumeration;

import javax.mail.Flags;
import javax.mail.MessagingException;
import javax.mail.internet.MimeMessage;

import org.apache.james.hibernate.entity.DbMailbox;
import org.apache.james.hibernate.entity.ImapUserInfo;
import org.apache.james.hibernate.entity.Mail;
import org.apache.james.hibernate.entity.MailFlag;
import org.apache.james.hibernate.entity.MailHeader;
import org.apache.james.imapserver.store.SimpleMessageAttributes;
import org.apache.james.mailboxmanager.GeneralMessageSet;
import org.apache.james.mailboxmanager.MailboxManagerException;
import org.apache.james.mailboxmanager.MessageResult;
import org.apache.james.mailboxmanager.SearchParameters;
import org.apache.james.mailboxmanager.impl.MessageResultImpl;
import org.apache.james.mailboxmanager.tracking.UidChangeTracker;
import org.apache.log4j.Logger;

import com.sun.mail.util.CRLFOutputStream;

public class ImapMailboxImpl extends AbstractImapMailbox {
	
	private final static Logger LOG = Logger.getLogger(ImapMailboxImpl.class);
	
	DbMailbox mailbox;
	MailboxPersistenceManager manager;
	ImapUserInfo user;

	public ImapMailboxImpl(MailboxPersistenceManager manager,DbMailbox mailbox,UidChangeTracker tracker, ImapUserInfo user) {
		super(tracker, mailbox.getUidValidity());
		this.manager = manager;
		this.mailbox = mailbox;
		this.user = user;
	}

	@Override
	public long getUidValidity() throws MailboxManagerException {
		checkAccess();
		return mailbox.getUidValidity();
	}

	@Override
	public MessageResult appendMessage(MimeMessage message, Date internalDate,
			int resultFlags) throws MailboxManagerException {
		LOG.info("appendMessage (message, internalDate, result):"+internalDate+','+resultFlags);
		if ((resultFlags & ~(MessageResult.UID | MessageResult.KEY | MessageResult.FLAGS)) != 0 ) {
			throw new MailboxManagerException("appendMessage failed, unsupported result flags:"+resultFlags);
		}
		checkAccess();
		
		try {
			Mail mail = new Mail();
			mail.setInternalDate(internalDate);
			mail.setNamespace(mailbox.getNamespace());
			mail.setKey(message.getMessageID());
			//mail.setSize(message.getSize());
			
		
			{ // calculate size
				CounterOutputStream cos = new CounterOutputStream();
				message.writeTo(new CRLFOutputStream(cos));
				mail.setSize(cos.getSize());
			}
			
			{
				// calculate envelope/body structure information
				SimpleMessageAttributes attr = new SimpleMessageAttributes(message);
				mail.setEnvelope(attr.getEnvelope());
				mail.setBodyStructure(attr.getBodyStructure(false));
			}
			
			int line_number = 0;
			
            for (Enumeration lines = message.getAllHeaderLines(); lines
					.hasMoreElements();) {
				String line = (String) lines.nextElement();
				int colon = line.indexOf(": ");
				if (colon > 0) {
					line_number++;
					MailHeader mh = new MailHeader ();
					mh.setLineNo(line_number);
					mh.setName(line.substring(0, colon));
					mh.setValue(line.substring(colon + 2));
					mail.addHeader(mh);
				}
			}


			
			MailFlag mf = manager.appendAsNewMail(user, mailbox, mail, message.getFlags());
			
			// persist the content to a possibly separate repository
			
			manager.getMailStore().persistMailBody(mf.getMail(), getByteBuffer(message));
			
			MessageResultImpl result = new MessageResultImpl(mail.getUid(), mf.getFlagsObject());
			result.setKey(mail.getKey());
			return result;
			
		} catch (MessagingException e) {
			throw new MailboxManagerException("appendMessage failed:"+e.getMessage(),e);
		} catch (IOException e) {
			throw new MailboxManagerException("appendMessage failed:"+e.getMessage(),e);
		}
		
		//throw new MailboxManagerException("appendMessage (message, internalDate, result):"+internalDate+','+result);
	}

	@Override
	public MessageResult[] getMessages(GeneralMessageSet set, int result)
			throws MailboxManagerException {
		LOG.info("getMessages (set, result):"+set+','+result);
		if ((result & (~(MessageResult.FLAGS | MessageResult.UID)))!=0) {
			//throw new MailboxManagerException("getMessages (set, result):"+set+','+result);
			final int FULL = (MessageResult.MIME_MESSAGE | MessageResult.BODY_CONTENT | MessageResult.FULL_CONTENT);
			return manager.getMessages(set, user, mailbox, (result & FULL ) != 0, (result & MessageResult.HEADERS) != 0);
		} else {
			// just UID/ KEY
			return manager.getMessagesFlags(set, user, mailbox);
		}
	}

	@Override
	public void remove(GeneralMessageSet set) throws MailboxManagerException {
		LOG.warn("remove (set):"+set);
		throw new MailboxManagerException("remove "+set);
	}

	@Override
	public MessageResult updateMessage(GeneralMessageSet messageSet,
			MimeMessage message, int result) throws MailboxManagerException {

		LOG.warn("updateMessage (messageSet, message, result):"+messageSet+','+message+','+result);
		throw new MailboxManagerException("updateMessage (messageSet, message, result):"+messageSet+','+message+','+result+" not supported!");
		
	}

	@Override
	public int getMessageCount() throws MailboxManagerException {
		checkAccess();
		return manager.getMessageCount(mailbox, user, null);
	}

	@Override
	public String getName() throws MailboxManagerException {
		return mailbox.getName();
	}

	@Override
	public MessageResult[] expunge(GeneralMessageSet set, int result)
			throws MailboxManagerException {
		LOG.warn("expunge (set, result):" + set + "," + result);
		if (set.getType()==GeneralMessageSet.TYPE_ALL) {
			manager.expungeAll(mailbox, user);
			return new MessageResult[0];
		} else {
			throw new MailboxManagerException("expunge (set, result):" + set + "," + result+" not supported!");
		}
	}

	@Override
	public MessageResult getFirstUnseen(int result)
			throws MailboxManagerException {
		// TODO Auto-generated method stub
		LOG.info("getFirstUnseen (result):"+result);
		if ((result & ~MessageResult.UID) != 0) {
			throw new MailboxManagerException("getFirstUnseen with flag: "+result+" not supported!");
		}
		
		Long unseenUid = manager.getFirstUnseenUid(mailbox, user);
		if (unseenUid!=null) {
			return new MessageResultImpl(unseenUid);
		}
		return null;
	}

	@Override
	public int getRecentCount(boolean reset) throws MailboxManagerException {
		LOG.info("getRecentCount (reset):"+reset);
		return manager.getMessageCount(mailbox, user,  "recent = true");
	}

	@Override
	public int getUnseenCount() throws MailboxManagerException {
		LOG.info("getUnseenCount ():");
		return manager.getMessageCount(mailbox, user, "seen = false");
	}


	@Override
	protected void setFlags(Flags flags, boolean addFlags, boolean replace,
			MessageResult[] messageList, GeneralMessageSet set) throws MailboxManagerException {
		LOG.warn("setFlags (flags, addFlags, replace, messageList, set):"+flags+','+addFlags+','+replace+','+messageList+','+set);
		manager.setFlags(flags, replace, addFlags, set, mailbox, user);
//		throw new MailboxManagerException("setFlags not implemented!");
	}
	
	@Override
	public MessageResult[] search(GeneralMessageSet set, SearchParameters searchTerm,
			int result) throws MailboxManagerException {
		LOG.warn("search (set, searchTerm, result):"+set+','+searchTerm+','+result);
		if ((result & ~MessageResult.UID) != 0) {
			throw new MailboxManagerException("search with flag: "+result+" not supported["+set+','+searchTerm+','+result+"]");
		}
		return manager.searchUids(mailbox, user, searchTerm);
		//throw new MailboxManagerException("search not implemented!");
	}

	@Override
	protected boolean checkMailboxExists() {
		DbMailbox freshMailbox = manager.getMailbox(mailbox.getId());
		if (freshMailbox!=null) {
			mailbox = freshMailbox;
			return true;
		}
		return false;
	}

	@Override
	protected long getMailboxLastUid() {
		return mailbox.getUid();
	}

}
